using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace XnaUtility.Camera
{
    /// <summary>
    /// So called because it mimicks the perspective camera in the Unreal Tournament 3 editor
    /// Unreal Tournament 3 is a copyright of Epic Games
    /// Epic Games is in no way associated with any part of this library
    /// LMB - turn left/right move forward/back
    /// RMB - Free look, up/down/left/right
    /// LMB + RMB - move left/right/forward/back
    /// </summary>
    public class UnrealEditCamera : CameraBase
    {

        private Control.InputBase input;
        Vector3 direction;
        Vector3 rotation;
        float turnSpeed;
        float moveSpeed;

        /// <summary>
        /// Constructs a camera that mimics the camera in perspective view in the UE3 Editor.
        /// </summary>
        /// <param name="game">Reference to the game</param>
        /// <param name="fov">The field of view in radians</param>
        /// <param name="ar">Aspect ratio of the screen</param>
        /// <param name="near">Near plane</param>
        /// <param name="far">Far plane</param>
        public UnrealEditCamera(Game game, float fov, float ar, float near, float far)
            : base(game, fov, ar, near, far)
        {
            direction = new Vector3(0, 0, -1);
            Location = new Vector3(0, 0, 0);
            rotation = new Vector3(0, 0, 0);
        }

        public override void Initialize()
        {
            input = (XnaUtility.Control.InputBase)Game.Services.GetService(typeof(Control.InputBase));

            if (input == null) throw new Exception("KeyboardCamera failed to get input service");

            turnSpeed = -0.001f;    //must be negetive, or you turn the wrong way
            moveSpeed = 0.25f;

            base.Initialize();
        }

        public override void Update(GameTime gameTime)
        {

            if (input.IsMouseButtonDown(XnaUtility.Control.MouseButton.LeftButton) && input.IsMouseButtonDown(XnaUtility.Control.MouseButton.RightButton))
            {
                Vector3 move = Vector3.Zero;
                move.Y = input.MouseDelta.Y * -moveSpeed;
                Location += move;

                float flip = 0;

                if (direction.X < 0) flip = -1;
                else flip = 1;

                move.Y = 0.0f;
                move.X = input.MouseDelta.X * -moveSpeed;
                rotation.Y = flip * (float)Math.Acos(Vector3.Dot(Vector3.UnitZ, direction));
                move = Vector3.Transform(move, Matrix.CreateFromAxisAngle(Vector3.UnitY, rotation.Y));
                Location += move;
            }
            else if (input.IsMouseButtonDown(XnaUtility.Control.MouseButton.LeftButton))
            {
                rotation.Y = input.MouseDelta.X * turnSpeed;
                direction = Vector3.Transform(direction, Matrix.CreateFromAxisAngle(Vector3.UnitY, rotation.Y));
                direction.Normalize();
                Vector3 move = direction * input.MouseDelta.Y * -moveSpeed;
                move.Y = 0.0f;
                Location += move;
            }
            else if (input.IsMouseButtonDown(XnaUtility.Control.MouseButton.RightButton))
            {
                rotation.Y = input.MouseDelta.X * turnSpeed;
                rotation.X = input.MouseDelta.Y * turnSpeed;
                direction = Vector3.Transform(direction, Matrix.CreateFromAxisAngle(Vector3.UnitY, rotation.Y));
                direction.Normalize();
                Vector3 axis = direction;
                axis = Vector3.TransformNormal(axis, Matrix.CreateFromAxisAngle(Vector3.UnitY, MathHelper.ToRadians(-90)));
                direction = Vector3.Transform(direction, Matrix.CreateFromAxisAngle(axis, rotation.X));
                direction.Normalize();
            }

            target = Location + direction;

            base.Update(gameTime);
        }



    }
}
